#include <iostream>

//#include "config.h"
#include "SysPlusPlus/Logger.h"
#include "SysPlusPlus/syscall.h"

#include "ComPlusPlus/Socket.h"
#include <errno.h>
#include "SysPlusPlus/ComException.h"

#include <arpa/inet.h>

#include <sys/socket.h>
#include <sys/un.h>

/**
 * Constructor. Sets default parameters.
 **/
compp::Socket::Socket ( ) : Comm (_CommDefaultReadAheadBufferSize) {

  compp::GenCfg * Cfg = (compp::GenCfg*) compp::GenCfg::Instance(); 

  this->UseDns        = Cfg->GetCommUseDNS()          ;  
  this->LingerSeconds = Cfg->GetCommLingerTime();

}

compp::Socket::Socket ( unsigned int buffsz   ) : Comm ( buffsz ) {

  compp::GenCfg * Cfg = (compp::GenCfg*) compp::GenCfg::Instance(); 

  this->UseDns        = Cfg->GetCommUseDNS()          ;  
  this->LingerSeconds = Cfg->GetCommLingerTime();

}

/**
 * Destructor
 **/
compp::Socket::~Socket ( ) {

}

/**
 * Analyses addr. If it is a hostname, tries to resolve it through DNS
 * if not assumes it is dotted decimal format.
 * Creates a IPV4 struct in_addr and places it in to ad
 * If addr cannot be converted to struct in_addr ComException is thrown
 **/
void compp::Socket::_MkIPAddrIPV4( const std::string &addr, struct in_addr *ad ) {
    struct hostent *hent;


    in_addr_t t;
    if ( (t = inet_addr ( addr.c_str() )) != INADDR_NONE ) {
      memcpy ( (void*) ad, (void*)&t, sizeof(t)) ;
      return;
    }
    if ( t == INADDR_NONE ) {
      if ( this->GetUseDNS() ) {
	if (NULL == (hent = gethostbyname ( addr.c_str() ))) {
	  throw  syspp::ComException (  "compp::Socket::_MkIPAddrIPV4(). Could not resolve name.");
	} else {
	  std::memmove( (void*) ad, (void*)  *hent->h_addr_list , sizeof(struct in_addr));
	}
      } else {
	throw  syspp::ComException (  "compp::Socket::_MkIPAddrIPV4() Cannot resolve address and resolver not used" );
      }
    }
}



/**
 * Analyses addr. If it is a hostname, tries to resolve it through DNS
 * if not assumes it is dotted decimal format.
 * Creates a IPV4 struct in_addr_in and places it in to ad
 * If addr cannot be converted to struct in_addr_in ComException is thrown
 **/
void compp::Socket::_MkSockaddrIPV4( const std::string &addr, const int port, struct sockaddr_in *ad ) {
    struct hostent *hent;
    struct in_addr adv4;

            in_addr_t t;
    if ( (t = inet_addr ( addr.c_str() )) != INADDR_NONE ) {
       memcpy ( (void*) ad, (void*)&t, sizeof(t)) ;
    }
    adv4.s_addr = t;
    if ( t == INADDR_NONE ) {

      if ( this->GetUseDNS() ) {

		if (NULL == (hent = gethostbyname ( addr.c_str() ))) {
			  throw  syspp::ComException (  "compp::SocketTcp::Connect(). Could not resolve name.");
		} else {
			  std::memmove( (void*) &(adv4), (void*)  *hent->h_addr_list , sizeof(adv4));
	  		  ad->sin_addr.s_addr =  adv4.s_addr;		
		}
      } else {
	throw  syspp::ComException (  "compp::Socket::_MkSockaddrIPV4() Cannot resolve address and resolver not used" );
      }
    } else {
      ad->sin_addr.s_addr = adv4.s_addr;		
    }

    ad->sin_family = AF_INET;
    ad->sin_port   = htons(port);
}

/**
  * Set LingerTime in Seconds.
 **/
bool compp::Socket::SetSockOptLinger ( int secs ) {
  

  if ( this->fd == -1 )
    return false;

  struct linger li;

  if ( 0  == (secs) ) {
    li.l_onoff = 0;
    li.l_linger = 0;
  } else {
    li.l_onoff = 1;
    li.l_linger = secs;
    }
  
  if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_LINGER, (void*)&li, sizeof(li) )) {
	return false;
  }
    
  return true;
}

/**
 *   Socket Option reuseaddress.
 **/

bool compp::Socket::SetSockOptReuseAddress ( bool yesno ) {
    int opt;

    if ( yesno == true ) 
	opt = 1;
    else
	opt = 0;

    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt) )) {
		return false;
    }

    return true;
}

/**
 *   Socket Option Keep Alive.
 */

bool compp::Socket::SetSockOptKeepAlive ( bool yesno ) {
    int opt;

    if ( yesno == true ) 
	opt = 1;
    else
	opt = 0;

    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&opt, sizeof(opt) )) {
		return false;
    } 

    return true;
}

/**
 * Sets option Oobinline
 */
bool compp::Socket::SetSockOptOobinline( bool yes ) { 
    int opt;

    if ( yes == true ) 
	opt = 1;
    else
	opt = 0;

#ifndef SO_OOBINLINE
 return false;
#else
    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_OOBINLINE, (void*)&opt, sizeof(opt) )) {
		return false;
    } 
#endif

    return true;
}

/**
 * Sets option SetSockOptDontroute
 * 
 **/
bool compp::Socket::SetSockOptDontroute (bool yes ) { 
    int opt;

    if ( yes == true ) 
	opt = 1;
    else
	opt = 0;

    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_DONTROUTE, (void*)&opt, sizeof(opt) )) {
		return false;
    } 

    return true;
}

/**
 * Sets send buffer size ( SndBuf ).
 * 
 * */

bool compp::Socket::SetSockOptSndBuf ( int bytes ) { 
    int opt;
	opt = bytes;
    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_SNDBUF, (void*)&opt, sizeof(opt) )) {
		return false;
    } 
    return true;
}

/**
 * Sets receive buffer size ( RcvBuf ).
 * 
 **/
bool compp::Socket::SetSockOptRcvBuf ( int bytes) { 
    int opt;
	opt = bytes;
    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_RCVBUF, (void*)&opt, sizeof(opt) )) {
		return false;
    } 
    return true;
}

/**
 * Sets Socket Priority
 * 
 **/
bool compp::Socket::SetSockOptPriority( bool yes) { 

    int opt;

    if ( yes == true ) 
	opt = 1;
    else
	opt = 0;

#ifndef SO_PRIORITY
 return false;
#else
    if ( 0 != syspp::Call::Setsockopt (this->fd, SOL_SOCKET, SO_PRIORITY, (void*)&opt, sizeof(opt) )) {
		return false;
    } 
#endif

    return true;
}


/**
 * Connect Socket to addr:port
 **/
bool compp::Socket::_Connect( const std::string &addr, const int port ){
    int  ret ;
    struct sockaddr_in ad;

    _MkSockaddrIPV4(addr, port, &ad);

    errno = 0;
    ret = syspp::Call::Connect ( this->fd, (struct sockaddr*) &ad, sizeof(struct sockaddr_in));

    if (ret == -1 && errno != EINPROGRESS ) {
      return false;
    }

    this->IsOpen=true;
    return true;
}
/**
 * Send len bytes of character buffer to socket 
 * Flags are a combination of
 * compp::Flg_MSG_OOB:  Sends  out-of-band  data  on  sockets that support this notion 
 * (TCP Sockets)
 * 
 * compp::FlgMSG_DONTROUTE: Don't use a gateway to send out the packet, only send to  
 * hosts on directly connected  networks.  This  is usually  used  only  by  diagnostic 
 * or routing programs. 
 * compp::FlgMSG_DONTWAIT Enables non-blocking operation; if the operation would block,  
 * EAGAIN  is  returned  (this  can  also  be enabled using the O_NONBLOCK with the 
 * F_SETFL fcntl(2)).
 * compp::FlgMSG_NOSIGNAL Requests  not  to send SIGPIPE on errors on stream oriented 
 * sockets when the other end breaks the connection. The EPIPE error is still returned.
 * compp::FlgMSG_CONFIRM (Linux 2.3+ only) Tell the link layer that forward progress 
 * happened: you got a successful reply from the  other  side.  If
 * the  link  layer  doesn't  get this it'll regularly reprobe the neighbour 
 * (e.g. via a unicast ARP).  Only valid on SOCK_DGRAM and SOCK_RAW sockets and 
 * currently only implemented for IPv4 and IPv6. See arp(7) for details.
 */
int compp::Socket::Send ( const void *buf, size_t len, int flags=0) {
int ret;
 ret = syspp::Call::Send(this->fd, buf, len, flags);
 if ( ret == -1 ) {
   perror ("UDP WRITE");
   std::cout << "Comm Errno " << errno   << "\n";
   throw syspp::ComException ( "Error in send procedure" );
 }
 return ret;     
}

/**
 * Send with flags = 0
 **/
int compp::Socket::Send ( const void *buf, size_t len){ 
	int ret;
	 
	ret = syspp::Call::Send(this->fd, buf, len, 0) ;
	if ( ret == -1 ) {
	  perror ("UDP WRITE");
	  std::cout << "Comm Errno " << errno << "\n";
	  throw syspp::ComException ( "Error in send procedure" );
    }
   return ret;     
}


/**
 * Receives maximum len bytes and stores the in buf. Flags can be set.
 */
int compp::Socket::Recv(void *buf, int len, int flags) {
int ret ;
	ret = syspp::Call::Recv(this->fd, buf, len, flags);
	if ( ret == -1 && errno != EAGAIN  ) {
		throw syspp::ComException( "Error in Recvfrom");
	}
	return ret;
}

/**
 * Receives maximum len bytes and stores the in buf. Flags can be set.
 * Sender is stored in sockaddr
 * Returns the number of bytes read or throws exception.
 */
int compp::Socket::Recvfrom(void *buf, int len, int flags, struct sockaddr *from, int *fromlen){
int ret;
	ret = syspp::Call::Recvfrom(this->fd, buf, len, flags, from, fromlen);	

	if ( ret == -1  && errno != EAGAIN  ) {
		throw syspp::ComException( "Error in Recvfrom");
	}

	return ret;
}


/**
 * Modifier
 **/
void compp::Socket::SetLingerTime ( int secs ) {
  LingerSeconds = secs ;
}

/**
 * Accessor
 **/
int compp::Socket::GetLingerTime ( ) const {
  return LingerSeconds;
}


/**
 * Accessor
 **/
bool compp::Socket::GetUseDNS () const {
  return this->UseDns;
}

/**
 * Modifier
 **/
void compp::Socket::SetUseDNS (bool val) {
  this->UseDns = val;

}


void compp::Socket::SetContextString ( std::string & s ) {
  ContextString = s;
}

std::string compp::Socket::GetContextString (  ) {
  return ContextString;
}




const std::string  compp::Socket::GetSocketAddress() const {

    return SocketAddress ;

}

const int compp::Socket::GetSocketPort() const {


  return SocketPort ;


}



